package org.lagou.service;

import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.DigestUtils;
import org.springframework.util.ResourceUtils;
import sun.security.provider.MD5;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class UserService {
    static Map<String, String> userinfoMap = new ConcurrentHashMap<>();
    static {
        try {
            File file = ResourceUtils.getFile("classpath:userInfo.json");
            try (FileInputStream fin = new FileInputStream(file)) {
                byte[] buffer = new byte[fin.available()];
                fin.read(buffer);
                String json = new String(buffer, "UTF-8").trim();
                userinfoMap = JSON.parseObject(json, Map.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Map<String, Object> login(String username, String password) {
        Map<String, Object> respMap = new HashMap<>();
        if (username == null || !userinfoMap.containsKey(username)) {
            respMap.put("state", 1000001);
            respMap.put("message", "用户不存在");
        } else if (password == null || !password.equals(userinfoMap.get(username))) {
            respMap.put("state", 1000002);
            respMap.put("message", "密码错误");
        } else {
            long failureTime = System.currentTimeMillis() + (24 * 60 * 60 * 1000);
            respMap.put("state", 0);
            respMap.put("message", "登录成功");
            respMap.put("token", createToken(username, password, failureTime));
        }
        return respMap;
    }

    public boolean isLogin(HttpServletRequest request) {
        Token token = parse(getToken(request));
        return token != null && token.isVerifyOk(userinfoMap.get(token.getUsername()));
    }

    public String getToken(HttpServletRequest request) {
        String token = request.getHeader("token");
        if (token == null || token.isEmpty()) {
            token = request.getParameter("token");
        }
        if (token == null) {
            Cookie[] cookies = request.getCookies();
            for (Cookie cookie : cookies) {
                if(cookie.getName().equals("token")) {
                    token = cookie.getValue();
                    break;
                }
            }

        }
        if (token != null) {
            token = token.trim();
        }
        return token;
    }

    public void loginFailJsonOut(HttpServletResponse response) {
        try {
            Map<String, Object> respMap = new HashMap<>();
            respMap.put("state", 1100001); // 登录失败
            respMap.put("message", "登录校验失败");
            response.setContentType("application/json;charset=UTF-8");
            PrintWriter writer = response.getWriter();
            writer.write(JSON.toJSONString(respMap));
            writer.flush();
            writer.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public void loginFailJump(HttpServletRequest request, HttpServletResponse response) {
        try {
            request.getRequestDispatcher("/index.jsp").forward(request, response);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private String createToken(String username, String password, long failureTime) {
        byte[] usernameBytes = username.getBytes(Charset.forName("UTF-8"));
        byte[] passwordBytes = (password + failureTime).getBytes(Charset.forName("UTF-8"));
        byte[] verifyKey = DigestUtils.md5DigestAsHex(passwordBytes).getBytes(Charset.forName("UTF-8"));
        ByteBuffer byteBuffer = ByteBuffer.allocate(4 + usernameBytes.length + verifyKey.length + 8);
        byteBuffer.putShort((short)usernameBytes.length);
        byteBuffer.putShort((short)verifyKey.length);
        byteBuffer.put(usernameBytes);
        byteBuffer.put(verifyKey);
        byteBuffer.putLong(failureTime);
        System.out.println();
        return Base64Utils.encodeToString(byteBuffer.array());
    }

    public Token parse(String token) {
        if (token == null || token.isEmpty()) {
            return null;
        }
        try {
            byte[] tokenBytes = Base64Utils.decodeFromString(token);
            if (tokenBytes.length < 12) {
                return null;
            }
            ByteBuffer byteBuffer = ByteBuffer.wrap(tokenBytes);
            int usernameLen = byteBuffer.getShort();
            int verifyKeyLen = byteBuffer.getShort();
            if (tokenBytes.length < 12 + usernameLen + verifyKeyLen) {
                return null;
            }
            String username = readString(byteBuffer, usernameLen);
            String verifyKey = readString(byteBuffer, verifyKeyLen);
            long failureTime = byteBuffer.getLong();

            Token tokenx = new Token();
            tokenx.username = username;
            tokenx.verifyKey = verifyKey;
            tokenx.failureTime = failureTime;
            return tokenx;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static class Token {
        private String username;
        private String verifyKey;
        private long failureTime;
        boolean isVerifyOk(String password) {
            return password != null && System.currentTimeMillis() < failureTime &&
            DigestUtils.md5DigestAsHex((password + failureTime).getBytes(Charset.forName("UTF-8"))).equals(verifyKey);
        }
        public String getUsername() {
            return username;
        }
    }

    private String readString(ByteBuffer byteBuffer, int len) {
        byte[] bytes = new byte[len];
        byteBuffer.get(bytes);
        return new  String(bytes, Charset.forName("UTF-8"));
    }
}
